'''
本程序简单预测PM2.5的走向，每个月只取了20天的测量数据。
共有18中污染源，即将 18种污染源的数据和对应的PM2.5的数据 进行训练
再根据训练结果来预测新的数据下PM2.5的值
'''
#   import library  #
import csv 
import numpy as np
from numpy.linalg import inv
import random
import math
import sys

#   read data   #
data = []
# 每一个维度存储一种污染物的数据,一共有18种污染物
for i in range(18):
	data.append([]) # []表示这十八个输入中，每一个输入都是一个列表

n_row = 0 # 初始从第0行开始
# 打开数据文件,文件big5编码为繁体字
text = open('E:/Users/HP/Desktop/PM2.5Prediction/PM2.5Prediction/PM2.5Prediction/train.csv', 'r', encoding='big5')
# 读取名称为text的Excel文件，返回文件行的累加信息,类型为_csv.reader
row = csv.reader(text , delimiter=",") 
# r中保存了当前行的所有信息，r是一个列表，每次循环到这就更换到下一行
for r in row: 
    # 第0行没有数据信息
    if n_row != 0:
        # 每一列只有第3-27格有值(一天内24小时的数值)
        for i in range(3,27):
            if r[i] != "NR":
                # data[]中一共可以存放18个列表，每一个列表存放某一种污染物的所有数值
                data[(n_row-1)%18].append(float(r[i])) 
            else:
                data[(n_row-1)%18].append(float(0))	
    n_row = n_row+1
text.close()
'''
file = open('E:/Users/HP/Desktop/PM2.5Prediction/PM2.5Prediction/PM2.5Prediction/testdata.csv','w', encoding = 'utf-8')
for i in range(len(data)):
    s = str(data[i]).replace('[','').replace(']','')#去除[],这两行按数据不同，可以选择
    s = s + '\n'   #去除单引号，逗号，每行末尾追加换行符
    file.write(s)
file.close()
print("保存文件成功") 
'''
#    Parse Data to (x,y)     #
x = []
y = []
# 一共有12个月，每个月20天
for i in range(12):
    # 一共有480个小时，连续取10个小时一组的数据，可取471组（最后9个数据舍去）
    for j in range(471):
        x.append([]) # 在x中加入列表存储数据
        # 污染物的种类一共有18种
        for t in range(18):
            # 取每种污染物前9小时的数据,在第10小时存放PM2.5的值
            for s in range(9): 
                x[471*i+j].append(data[t][480*i+j+s] )
        y.append(data[9][480*i+j+9]) # PM2.5的数据存放在第10行
# 每个按行排列
x = np.array(x)
y = np.array(y)

# add square term
# x = np.concatenate((x,x**2), axis=1) 将数据量翻倍，新的数据为x^2

# add bias
# 5652行1列的ones，每一行1后面直接连接x的每行列表
# 此处多的一列存储的是bias，值为1
x = np.concatenate((np.ones((x.shape[0],1)),x), axis=1) 
#print(x.shape) # (5652, 163)

#init weight & other hyperparams#
w = np.zeros(len(x[0])) # shap: (163,) 0: 163 __len__: 1 size: 163
l_rate = 10 # 学习率，用于更新w
repeat = 10000 # 迭代次数

#check your ans with close form solution#
# use close form to check whether ur gradient descent is good
# however, this cannot be used in hw1.sh 
# w = np.matmul(np.matmul(inv(np.matmul(x.transpose(),x)),x.transpose()),y)

'''模型是Y = b + w * x'''

#start training#
# 将x矩阵进行转置，此时第一行全是1
# shape: (5652,) 0: 5652 len: 1 size: 5652
x_t = x.transpose() 
s_gra = np.zeros(len(x[0])) # 163行的0

for i in range(repeat):
    hypo = np.dot(x,w) # 做矩阵乘法，(5652, 163)*(163, 1) shape = (5652, 1),预测值
    loss = hypo - y # 预测数值 - 真实数值
    cost = np.sum(loss**2) / len(x) # L(f) = (y - f)^2 求和再求平局值
    cost_a  = math.sqrt(cost) # 开方
    # 梯度下降
    gra = np.dot(x_t,loss) # (163, 5652) * (5652, 1)   shape: (163, 1)   dL
    s_gra += gra**2 
    ada = np.sqrt(s_gra) # 均方根
    w = w - l_rate * (gra/ada) # 更新后的w
    print ('iteration: %d | Cost: %f  ' % ( i,cost_a))

# save model
np.save('model.npy',w)
# read model
w = np.load('model.npy')

test_x = []
n_row = 0
text = open('E:/Users/HP/Desktop/PM2.5Prediction/PM2.5Prediction/PM2.5Prediction/test.csv' ,"r")
row = csv.reader(text , delimiter= ",")

for r in row:
    if n_row %18 == 0:
        test_x.append([]) # 没有18个数据就生成新的列表
        for i in range(2,11):
            test_x[n_row//18].append(float(r[i]) ) # // 表示向下取整
    else :
        for i in range(2,11):
            if r[i] !="NR":
                test_x[n_row//18].append(float(r[i]))
            else:
                test_x[n_row//18].append(0)
    n_row = n_row+1
text.close()
test_x = np.array(test_x)

# add square term
# test_x = np.concatenate((test_x,test_x**2), axis=1)

# add bias
test_x = np.concatenate((np.ones((test_x.shape[0],1)),test_x), axis=1) # 把test里面的数据连接到ones后面

ans = []
for i in range(len(test_x)):
    ans.append(["id_"+str(i)])
    a = np.dot(w,test_x[i]) # 用已经训练的w来生成预测值，并存入ans
    ans[i].append(a)

filename = "predict.csv" # 最后把结果写入predict的文件中
text = open(filename, "w+")
s = csv.writer(text,delimiter=',',lineterminator='\n')
s.writerow(["id","value"])
for i in range(len(ans)):
    s.writerow(ans[i]) 
text.close()
